package top.shiyiri.map;

import org.junit.Test;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedHashMap;

/**
 * @author Aunean
 * @date 2022/1/6
 */
/*
一：
Map接口：双列集合，用来存储一对（key-value）一对的数据
    HashMap：作为Map的主要实现类；线程不安全的，效率高；存储null的key和value
        LinkedHashMap：保证在遍历map元素时，可以按照添加的顺序实现遍历
            原因：在原来的HashMap底层结构基础上，添加了一对指针，指向前一个和后一个元素
            对于频繁的遍历操作，此类执行效率高于HashMap
    TreeMap：保证按照添加的key-value对进行排序，实现排序遍历。此时考虑key的自然排序或定制排序
            底层使用红黑树
    Hashtable：作为古老的实现类；线程安全的，效率低；不能存储null的key和value
        Properties：常用来处理配置文件。key和value都是String类型

    HashMap的底层：数组 + 链表（JDK8前）
                  数组 + 链表 + 红黑树（JDK8）

二、Map结构的理解：
    Map中的key：无序的、不可重复的、使用Set存储所有的Key  --->  key所在的类要重写equals() 和 hashCode()
    Map的中的value：无序的，可重复的，使用Collection存储所有的value --->  value所在的类要重写equals()
    一个键值对：key-value构成了一个Entry对象。
    Map中的entry：无序的，不可重复的，使用Set存储所有的entry。

三、HashMap的底层实现原理？
以JDK7为例说明：
    HashMap map = new HashMap();
    在实例化以后，底层创建了长度是16的一维数组 Entry[] table。
    ...可能已经执行过多次put....
    map.put(key1, value1)：
    首先，调用key1所在类的hashCode()计算key1哈希值，此哈希值经过某种算法计算以后，得到在Entry数组中的存放位置
    如果此位置上的数据为空，此时的key1-value1添加成功   ---  情况一
    如果此位置上的数据不为空，（意味着此位置上存在一个或多个数据（以链表形式存在）），比较key1和已经存在的一个或
    多个数据的哈希值：
        如果key1的哈希值与已经存在的数据的哈希值都不相同，此时key1-value1添加成功   ---  情况二
        如果key1的哈希值与已经存在的数据的哈希值相同，继续比较：
            调用key1所在类的equals()，比较：
                如果equals()返回false：此时key1-value1添加成功   ---  情况三
                如果equals()返回true：使用value1替换相同key的value值
     补充：关于情况二和情况三：此时的key1-value1和原来的数据以链表的方式存储
    在不断的添加过程中，会涉及到扩容问题，当超出临界值（且要存放的位置非空）时，扩容。默认的扩容方式：扩容为原来容量的两倍，并将原有的数据复制过来

JKD8相较于JDK7在底层实现方面的不同：
1.new HashMap()：底层没有创建一个长度为16的Entry数组
2.JDK8底层的数组是Node[]，而非Entry[]
3.首次调用put()时，底层创建长度为16的数组
4.JDK7底层结构：数组+链表。JDK8底层结构：数组+链表+红黑树
    当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时，
    此时此索引位置上的所有数据改为使用红黑树存储。

DEFAULT_INITIAL_CAPACITY : HashMap的默认容量，16
MAXIMUM_CAPACITY ： HashMap的最大支持容量，2^30
DEFAULT_LOAD_FACTOR：HashMap的默认加载因子 0.75
threshold：扩容的临界值，=容量*填充因子：16 * 0.75 = 12
TREEIFY_THRESHOLD：Bucket中链表长度大于该默认值，转化为红黑树
MIN_TREEIFY_CAPACITY：桶中的Node被树化时最小的hash表容量：64


四、LinkedHashMap的底层实现原理（了解）
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;//能够记录添加的元素的先后位置
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }


面试题：
1.HashMap 的底层实现原理？
2.HashMap 和 Hashtable的异同？
3.CurrentHashMap 与 Hashtable的异同？
 */
public class MapTest {

    @Test
    public void test1() {
        HashMap<Object, Object> map = new HashMap<>();
        map.put(null, 12);
        System.out.println(map);

        Hashtable<Object, Object> hashtable = new Hashtable<>();
        hashtable.put(null, 12);
        System.out.println(hashtable);//NullPointerException
    }

    @Test
    public void test2() {
        HashMap<Object, Object> map = new HashMap<>();
        map = new LinkedHashMap<>();
        map.put(123, "AA");
        map.put(345, "BB");
        map.put(12, "CC");

        System.out.println(map);
    }
}
